using System;

namespace SeaVM
{
    public enum DataType
    {
        Number,
        String,
        Boolean,
        Nil,
    }

    public class Data
    {
        public DataType type;
        public Number @number = new Number();
        public String @string = new String();
        public Boolean @boolean = new Boolean();
        public Nil @nil = new Nil();
        
        public Data()
        {
            type = DataType.Nil;
        }
        public Data(double number)
        {
            type = DataType.Number;
            this.number = new Number();
            this.number.value = number;

        }

        public Data(string str)
        {
            type = DataType.String;
            @string = new String();
            @string.value = str;
        }

        public Data(bool boolean)
        {
            type = DataType.Number;
            this.boolean = new Boolean();
            this.boolean.value = boolean;

        }

        public override string ToString()
        {
            switch(type)
            {
                case DataType.Number :return number.value.ToString();
                case DataType.String :return @string.value;
                case DataType.Boolean :return boolean.value.ToString();
                case DataType.Nil :return "nil";
                default:return "";
            }
        }

        public static Data Add(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Add(a.number,b.number);
                        case DataType.String  : return Number.Add(a.number,b.@string);
                        case DataType.Boolean : return Number.Add(a.number,b.boolean);
                        case DataType.Nil     : return Number.Add(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Add(a.@string,b.number);
                        case DataType.String  : return String.Add(a.@string,b.@string);
                        case DataType.Boolean : return String.Add(a.@string,b.boolean);
                        case DataType.Nil     : return String.Add(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Add(a.boolean,b.number);
                        case DataType.String  : return Boolean.Add(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Add(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Add(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Add(a.nil,b.number);
                        case DataType.String  : return Nil.Add(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Add(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Add(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Sub(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Sub(a.number,b.number);
                        case DataType.String  : return Number.Sub(a.number,b.@string);
                        case DataType.Boolean : return Number.Sub(a.number,b.boolean);
                        case DataType.Nil     : return Number.Sub(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Sub(a.@string,b.number);
                        case DataType.String  : return String.Sub(a.@string,b.@string);
                        case DataType.Boolean : return String.Sub(a.@string,b.boolean);
                        case DataType.Nil     : return String.Sub(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Sub(a.boolean,b.number);
                        case DataType.String  : return Boolean.Sub(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Sub(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Sub(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Sub(a.nil,b.number);
                        case DataType.String  : return Nil.Sub(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Sub(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Sub(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Mul(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Mul(a.number,b.number);
                        case DataType.String  : return Number.Mul(a.number,b.@string);
                        case DataType.Boolean : return Number.Mul(a.number,b.boolean);
                        case DataType.Nil     : return Number.Mul(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Mul(a.@string,b.number);
                        case DataType.String  : return String.Mul(a.@string,b.@string);
                        case DataType.Boolean : return String.Mul(a.@string,b.boolean);
                        case DataType.Nil     : return String.Mul(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Mul(a.boolean,b.number);
                        case DataType.String  : return Boolean.Mul(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Mul(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Mul(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Mul(a.nil,b.number);
                        case DataType.String  : return Nil.Mul(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Mul(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Mul(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Div(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Div(a.number,b.number);
                        case DataType.String  : return Number.Div(a.number,b.@string);
                        case DataType.Boolean : return Number.Div(a.number,b.boolean);
                        case DataType.Nil     : return Number.Div(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Div(a.@string,b.number);
                        case DataType.String  : return String.Div(a.@string,b.@string);
                        case DataType.Boolean : return String.Div(a.@string,b.boolean);
                        case DataType.Nil     : return String.Div(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Div(a.boolean,b.number);
                        case DataType.String  : return Boolean.Div(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Div(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Div(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Div(a.nil,b.number);
                        case DataType.String  : return Nil.Div(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Div(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Div(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Mod(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Mod(a.number,b.number);
                        case DataType.String  : return Number.Mod(a.number,b.@string);
                        case DataType.Boolean : return Number.Mod(a.number,b.boolean);
                        case DataType.Nil     : return Number.Mod(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Mod(a.@string,b.number);
                        case DataType.String  : return String.Mod(a.@string,b.@string);
                        case DataType.Boolean : return String.Mod(a.@string,b.boolean);
                        case DataType.Nil     : return String.Mod(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Mod(a.boolean,b.number);
                        case DataType.String  : return Boolean.Mod(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Mod(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Mod(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Mod(a.nil,b.number);
                        case DataType.String  : return Nil.Mod(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Mod(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Mod(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Gr(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Gr(a.number,b.number);
                        case DataType.String  : return Number.Gr(a.number,b.@string);
                        case DataType.Boolean : return Number.Gr(a.number,b.boolean);
                        case DataType.Nil     : return Number.Gr(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Gr(a.@string,b.number);
                        case DataType.String  : return String.Gr(a.@string,b.@string);
                        case DataType.Boolean : return String.Gr(a.@string,b.boolean);
                        case DataType.Nil     : return String.Gr(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Gr(a.boolean,b.number);
                        case DataType.String  : return Boolean.Gr(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Gr(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Gr(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Gr(a.nil,b.number);
                        case DataType.String  : return Nil.Gr(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Gr(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Gr(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Le(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Le(a.number,b.number);
                        case DataType.String  : return Number.Le(a.number,b.@string);
                        case DataType.Boolean : return Number.Le(a.number,b.boolean);
                        case DataType.Nil     : return Number.Le(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Le(a.@string,b.number);
                        case DataType.String  : return String.Le(a.@string,b.@string);
                        case DataType.Boolean : return String.Le(a.@string,b.boolean);
                        case DataType.Nil     : return String.Le(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Le(a.boolean,b.number);
                        case DataType.String  : return Boolean.Le(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Le(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Le(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Le(a.nil,b.number);
                        case DataType.String  : return Nil.Le(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Le(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Le(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Eq(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Eq(a.number,b.number);
                        case DataType.String  : return Number.Eq(a.number,b.@string);
                        case DataType.Boolean : return Number.Eq(a.number,b.boolean);
                        case DataType.Nil     : return Number.Eq(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Eq(a.@string,b.number);
                        case DataType.String  : return String.Eq(a.@string,b.@string);
                        case DataType.Boolean : return String.Eq(a.@string,b.boolean);
                        case DataType.Nil     : return String.Eq(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Eq(a.boolean,b.number);
                        case DataType.String  : return Boolean.Eq(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Eq(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Eq(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Eq(a.nil,b.number);
                        case DataType.String  : return Nil.Eq(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Eq(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Eq(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }

        public static Data Neq(Data a,Data b)
        {
            switch(a.type)
            {
                case DataType.Number : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Number.Neq(a.number,b.number);
                        case DataType.String  : return Number.Neq(a.number,b.@string);
                        case DataType.Boolean : return Number.Neq(a.number,b.boolean);
                        case DataType.Nil     : return Number.Neq(a.number,b.nil);
                        default:return null;
                    }
                }
                case DataType.String : {
                    switch(b.type)
                    {
                        case DataType.Number  : return String.Neq(a.@string,b.number);
                        case DataType.String  : return String.Neq(a.@string,b.@string);
                        case DataType.Boolean : return String.Neq(a.@string,b.boolean);
                        case DataType.Nil     : return String.Neq(a.@string,b.nil);
                        default:return null;
                    }
                }
                case DataType.Boolean : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Boolean.Neq(a.boolean,b.number);
                        case DataType.String  : return Boolean.Neq(a.boolean,b.@string);
                        case DataType.Boolean : return Boolean.Neq(a.boolean,b.boolean);
                        case DataType.Nil     : return Boolean.Neq(a.boolean,b.nil);
                        default:return null;
                    }
                }
                case DataType.Nil : {
                    switch(b.type)
                    {
                        case DataType.Number  : return Nil.Neq(a.nil,b.number);
                        case DataType.String  : return Nil.Neq(a.nil,b.@string);
                        case DataType.Boolean : return Nil.Neq(a.nil,b.boolean);
                        case DataType.Nil     : return Nil.Neq(a.nil,b.nil);
                        default:return null;
                    }
                }
                default:return null;
            }
        }
}